package utils

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"github.com/gin-gonic/gin"
	"sync"
	"time"
)

/** cookie 操作工具*/

// 定义结构体
type CookieUtil struct {
}

// CookieContext 上下文，绑定到特定请求
type CookieContext struct {
	ctx  *gin.Context
	util *CookieUtil
}

// 单例
var (
	cookieUtilInstance *CookieUtil
	cookieUtilOnce     sync.Once
)

/**
 * GetCookieUtil 获取 CookieUtil 单例实例
 */
func GetCookieUtil() *CookieUtil {
	cookieUtilOnce.Do(func() {
		cookieUtilInstance = &CookieUtil{}
	})
	return cookieUtilInstance
}

/**
 * Use 创建绑定到特定请求的 Cookie 上下文
 * ：减少其它函数也有c *gin.Context入参
 */
func (cu *CookieUtil) Use(c *gin.Context) *CookieContext {
	return &CookieContext{
		ctx:  c,
		util: cu,
	}
}

/**
 * 生成cookie 并设置cookie
 * @Param key
 * @Param max 过期时长
 * @Return 生成的cookie
 */
func (cc *CookieContext) Generate(key string, max int) string {
	// 随机生成cookie
	bytes := make([]byte, max)
	_, err := rand.Read(bytes)
	if err != nil {
		// 如果随机数生成失败，使用时间戳作为备选方案
		return fmt.Sprintf("%d", time.Now().UnixNano())
	}
	val := hex.EncodeToString(bytes)

	// 设置 cookie
	// 将生成的val 作为cookie：key
	// maxAge 过期时长 单位=秒
	cc.ctx.SetCookie(key, val, max, "/", "", false, true)

	return val
}

/**
 * 校验cookie 是否存在
 * @Param key
 * @Return (string, bool) as (cookie值,是否存在)
 */
func (cc *CookieContext) CheckExist(key string) (string, bool) {
	val, err := cc.ctx.Cookie(key)
	if err != nil {
		return "", false
	}
	return val, true
}

/**
 * 获取cookie的值
 * @Param key
 * @Return string cookie值
 */
func (cc *CookieContext) GetCookie(key string) string {
	val, err := cc.ctx.Cookie(key)
	if err != nil {
		return ""
	}
	return val
}

/**
 * 设置cookie的值
 * @Param key, val, maxAge
 * @Return cookie值
 */
func (cc *CookieContext) SetCookie(key, val string) string {
	// 设置 cookie
	// 将生成的val 作为cookie：key
	// maxAge 过期时长 单位=秒
	var maxAge int = 1 * (24 * 60 * 60) // 1天
	//var maxAge int = 7 * (24 * 60 * 60)  // 七天
	cc.ctx.SetCookie(key, val, maxAge, "/", "", false, true)

	return val
}

/**
 * 删除cookie
 * @Param key
 * @Return 是否删除成功
 */
func (cc *CookieContext) DelCookie(key string) bool {
	_, err := cc.ctx.Cookie(key)
	if err != nil {
		return false
	}

	// 设置浏览器cookie过期
	cc.ctx.SetCookie(key, "", -1, "/", "", false, true)
	return true
}

/**
 * 删除所有cookie
 */
func (cc *CookieContext) DelAllCookies() {
	// 获取所有 cookie 名称
	for _, cookie := range cc.ctx.Request.Cookies() {
		// 将每个 cookie 的过期时间设置为过去的时间
		cc.ctx.SetCookie(cookie.Name, "", -1, "/", "", false, true)
	}
}

func cookie_test() {
	/* 使用方式：
	// 浏览器会自动删除过期 cookie，你不需要手动清理
	r.GET("/delCookie", func(c *gin.Context) {
		cookieCtx := utils.GetCookieUtil().Use(c)
		cookieCtx.SetCookie("key", "val")
		cookieCtx.GetCookie("key")
		cookieCtx.CheckExist("key")
		cookieCtx.DelAllCookies()
		c.JSON(200, OkWithMsg("success"))
	})*/
}
